/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.sys;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import junit.framework.AssertionFailedError;
/**
* This class implements a mock object for any service, using a dynamic proxy. It returns results for specific methods and
* arguments, and can also relay unspecified methods or arguments to a fallback service. Note that this proxy does not do Spring
* things like AOP transactions, altho the fallback service may.
*/
public class MockService implements InvocationHandler {
private final HashMap<String, MockMethod> nameToMockMethodMap = new HashMap<String, MockMethod>();
private final Object noMethodFallback;
private MockService(MockMethod[] mockMethods, Object noMethodFallback) {
this.noMethodFallback = noMethodFallback;
for (MockMethod m : mockMethods) {
nameToMockMethodMap.put(m.getName(), m);
}
}
/**
* Returns the result associated with the given method and list of arguments. If there is no mock method, invokes the given
* method on the noMethodFallback Object. If the noMethodFallback Object is null, throws a
* {@link junit.framework.AssertionFailedError}.
*
* @see java.lang.reflect.InvocationHandler#invoke
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if (nameToMockMethodMap.containsKey(name)) {
MockMethod m = nameToMockMethodMap.get(name);
return m.invoke(proxy, method, args);
}
if (noMethodFallback == null) {
throw new AssertionFailedError("no mock method " + name);
}
return method.invoke(noMethodFallback, args);
}
/**
* Creates a dynamic proxy with the given mock methods.
*
* @param iface the interface to proxy
* @param mockMethods the methods to mock
* @param noMethodFallback the Object to use when there is no mock method for the invoked method, or null if a
* {@link junit.framework.AssertionFailedError} should be thrown in this case instead.
* @return a dynamic proxy implementing the given interface
*/
public static <I> I createProxy(Class<I> iface, MockMethod[] mockMethods, Object noMethodFallback) {
// noinspection unchecked
return (I) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new MockService(mockMethods, noMethodFallback));
}
/**
* Creates a dynamic proxy with a single mock method that has a single result. Invocations of other methods or arguments will
* throw a {@link junit.framework.AssertionFailedError}.
*
* @param iface the interface to proxy
* @param methodName the name of the method to mock
* @param args the arguments to expect for the named method
* @param result the result to return from the named method
* @return a dynamic proxy implementing the given interface
*/
public static <I> I createProxy(Class<I> iface, String methodName, Object[] args, Object result) {
MockMethod mockMethod = new MockMethod(methodName, null);
mockMethod.setResult(result, args);
return createProxy(iface, new MockMethod[] { mockMethod }, null);
}
}